package com.eschool.service.impl;

import java.sql.Time;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.hibernate.envers.strategy.ValidTimeAuditStrategy;

import com.eschool.dao.ClassDao;
import com.eschool.dao.CourseDao;
import com.eschool.dao.RoomDao;
import com.eschool.dao.ScheduleDao;
import com.eschool.dao.SectionDao;
import com.eschool.dao.TeacherDao;
import com.eschool.model.Branch;
import com.eschool.model.Clas;
import com.eschool.model.Course;
import com.eschool.model.Room;
import com.eschool.model.ScheduleSlot;
import com.eschool.model.Section;
import com.eschool.model.Teacher;
import com.eschool.service.TimeTableService;
import com.eschool.util.ConstraintsUtil;
import com.eschool.util.UserUtil;

public class TimeTableServiceImpl implements TimeTableService {

	@Override
	public String makeTimetable(List<Section> sections, List<Teacher> teachers,
			List<Room> rooms, Time startTime, Time endTime, Time breakStartTime, Time breakEndTime) {
		
		// Clearing the table if there exist some rows in it
		ScheduleDao.clearSchedule(UserUtil.currentUser().getBranch().getBranchId());
		
		// Now making timetable
		Map m = new HashMap<String, Integer>();
		
		for (Iterator iterator = sections.iterator(); iterator.hasNext();) {
			Section s = (Section) iterator.next();
			for (Iterator iterator2 = s.getClas().getCourses().iterator(); iterator2.hasNext();) {
				Course c = (Course) iterator2.next();
				m.put(""+s.getSectionId()+c.getCourseId(), (int)c.getHoursPerWeek());
			}
		}
		
		Map classCourses = new HashMap<Object, List<Course>>();
		List<Clas> classes = ClassDao.getClasses(null);
		for (Iterator iterator = classes.iterator(); iterator.hasNext();) {
			Clas clas = (Clas) iterator.next();
			classCourses.put(clas.getClassId(), sortCourses(
									new ArrayList<Course>(clas.getCourses()), teachers) );
		}
		
		//int day = 1;
		
		//long classDuration = 40 * 60 * 1000;
		
		//List<ScheduleSlot> timetableList = new ArrayList<ScheduleSlot>();
		
		ScheduleSlot scheduleSlot = new ScheduleSlot();
		
		for(int day=1; day<=6; day++)
		{
		
			System.out.println("Day " + day);
		for (Iterator iterator = sections.iterator(); iterator.hasNext();) {
			
			Section currentSection = (Section) iterator.next();
			
			System.out.println("\tTracing class " + currentSection.getClas().getClassTitle() + " " + currentSection.getSectionTitle());
			List courses = new ArrayList<Course>((List<Course>) classCourses.get(currentSection.getClas().getClassId()));

			ScheduleSlot tempSlot = new ScheduleSlot();
			
			Time classStartTime = startTime;
			
			boolean twice = false;	Course courseTwice = null; Teacher	teacherTwice = null;
			
Section:	for (int i=0; i<courses.size(); i++) {
				
				Course currentCourse = (Course) courses.get(i);
				System.out.println("\t\tTracing course " + currentCourse.getTitle());
				List<Teacher> validTeacherList = new ArrayList<Teacher>();
				if((Integer)m.get(""+currentSection.getSectionId()+currentCourse.getCourseId())!=0){

				Time classEndTime = new Time( classStartTime.getTime() + currentCourse.getClassInterval() * 60 * 1000);
				
				int roomsCount = 0;
Course:			for (Iterator iterator2 = rooms.iterator(); iterator2.hasNext();) {
					
					Room currentRoom = (Room) iterator2.next();
					System.out.println("\t\t\tTracing Room " + currentRoom.getRoomTitle());
					roomsCount++;
					
Room:				if (currentRoom.getCapacity() >= currentSection.getStrength()  && (ScheduleDao.isRoomFree(currentRoom, classStartTime, classEndTime, day)) ){
							
						System.out.println("\t\t\t  Room " + currentRoom.getRoomTitle() + " matches the capacity and is free");
						if(isValidTime(classStartTime, classEndTime, startTime, endTime) &&
								!isBreakTime(classStartTime, classEndTime, breakStartTime, breakEndTime)){
							int teacherCount=0; 
							for (Iterator iterator4 = teachers.iterator(); iterator4.hasNext();) {
							
								Teacher currentTeacher = (Teacher) iterator4.next();
								System.out.println("\t\t\t\tTracing teacher " + currentTeacher.getName());
								teacherCount++;
Teacher:						for (Iterator iterator5 = currentTeacher.getCourses().iterator(); iterator5.hasNext();) {
									
									Course c = (Course) iterator5.next();
									
									if(c.getCourseId() == currentCourse.getCourseId()){
										validTeacherList.add(currentTeacher);
										System.out.println("\t\t\t\t  Teacher " + currentTeacher.getName() + " teaches " + currentCourse.getTitle());
										if (ScheduleDao.isFree(currentTeacher, classStartTime, classEndTime, day)) {
								
											System.out.println("\t\t\t\t  Teacher " + currentTeacher.getName() + " is free for " + classStartTime+"-"+classEndTime);
											scheduleSlot.setTeacher(currentTeacher);
											scheduleSlot.setRoom(currentRoom);
								
											Branch branch = new Branch();	branch.setBranchId(UserUtil.currentUser().getBranch().getBranchId());
								
											scheduleSlot.setBranch(branch);
											scheduleSlot.setSection(currentSection);
											scheduleSlot.setCourse(currentCourse);
											scheduleSlot.setStartTime(classStartTime);
											scheduleSlot.setEndTime(classEndTime);
											scheduleSlot.setDay(day);
											scheduleSlot.setValidFrom(new Date());
											scheduleSlot.setValidTill(new Date());
											
											ScheduleDao.addTempSlot(scheduleSlot);
											
											classStartTime = new Time(classEndTime.getTime());
											
											currentCourse.setHoursPerWeek(currentCourse.getHoursPerWeek()-1);

											
											int remainingClasses = (Integer) m.get(""+currentSection.getSectionId()+currentCourse.getCourseId()) - 1;
											m.put(""+currentSection.getSectionId()+currentCourse.getCourseId(), remainingClasses);
											
											
//											twice = false;	courseTwice = null;		teacherTwice = null;
											break Course;
									}
							
									else{
//										if(teacherCount==teachers.size()){
//											courses.add(currentCourse);
//											break Course;
//										}
										
//										else{
//											if(twice && (courseTwice.equals(currentCourse) || teacherTwice.equals(currentTeacher))){
//												twice = false;	courseTwice = null;		teacherTwice = null;
//												break Section;
//											}
//											twice = true;
//											courseTwice = currentCourse;
										
											// -- Solve here
											if(	( (courseTwice != null && courseTwice.equals(currentCourse)) ||
													(teacherTwice!=null && teacherTwice.equals(currentTeacher)) ))
												twice = true;
											if(!twice)
												break Teacher; 
									}
								}
							}
									
									if(teacherCount==teachers.size()){
										if(	( (courseTwice != null && courseTwice.equals(currentCourse)) ||
												(teacherTwice!=null && teacherTwice.equals(currentTeacher)) ) 
												&& validTeacherList.size() !=0 ){
											System.out.println(validTeacherList.size());
											List<ScheduleSlot> techersBusyList = ScheduleDao.teacehersBusyList(
																	validTeacherList, classStartTime, classEndTime, day);
											
											Time minTime = ((ScheduleSlot)techersBusyList.get(0)).getEndTime();
											
											for(int a=1; a<techersBusyList.size()-1; a++)
												if(((ScheduleSlot)techersBusyList.get(a)).getEndTime().getTime() < minTime.getTime())
													minTime = ((ScheduleSlot)techersBusyList.get(a)).getEndTime();
											classStartTime = minTime;
											
											courseTwice = null;
											teacherTwice = null;
											twice = false;
										}
										
										else{
											courseTwice = currentCourse;
											teacherTwice = currentTeacher;
										}
//										
										System.out.println("\t\t******* teacherCount==teacherSize()");
										courses.add(currentCourse);
										break Course;
									}
									
									// -- till here
//									else
//										break Teacher;
//							if(teacherCount==teachers.size()){
//							System.out.println("\t\t******* teacherCount==teacherSize()");
//							courses.add(currentCourse);
//							break Course;
							}
						}
						
						else if(isBreakTime(classStartTime, classEndTime, breakStartTime, breakEndTime)){
							System.out.println("BreakTime");
							classStartTime = breakEndTime;
							i--;
							break Course;
						}
						
						else 
							break Course;
					}
				else if(currentRoom.getCapacity() < currentSection.getStrength())
					;
					
				else if(!(ScheduleDao.isRoomFree(currentRoom, classStartTime, classEndTime, day)) && roomsCount==rooms.size()){
					List tempList1 = ScheduleDao.roomBusyList(rooms, classStartTime, classEndTime, day);
					System.out.println("Size: " + tempList1.size());
					Time minTime = ((ScheduleSlot)tempList1.get(0)).getEndTime();
					for(int a=1; a<tempList1.size()-1; a++)
						if(((ScheduleSlot)tempList1.get(a)).getEndTime().getTime() < minTime.getTime())
							minTime = ((ScheduleSlot)tempList1.get(a)).getEndTime();
					classStartTime = minTime;
					i--;
					break Course;
				}
				else
					;
				}
			}
		}
		}
		}
		System.out.println("Done");
		return "done";
	}
	
	boolean isValidTime(Time startTimeToCheck, Time endTimeToCheck, Time startTime, Time endTime){
		if( startTimeToCheck.getTime() < endTime.getTime() && endTimeToCheck.getTime() <= endTime.getTime() )
			return true;
		return false;
	}
	
	boolean isBreakTime(Time startTimeToCheck, Time endTimeToCheck, Time breakStartTime, Time breakEndTime){

		if( (startTimeToCheck.getTime() < breakStartTime.getTime() &&
				endTimeToCheck.getTime() <= breakStartTime.getTime()) ||
				(startTimeToCheck.getTime() >= breakEndTime.getTime() &&
						endTimeToCheck.getTime() > breakEndTime.getTime())
				)	
			
			return false;

		return true;
	}
	
	@Override
	public List<ScheduleSlot> getTimetable() {
		return ScheduleDao.getSchedules(ConstraintsUtil.getConstraintsMap());
	}
		
		@Override
		public String prepareTimeable(Date schoolStart, Date schoolEnd, Date breakStart, Date breakEnd) {
			
			Time schoolStartTime = new Time(schoolStart.getTime());
			Time schoolEndTime = new Time(schoolEnd.getTime());
			Time breakStartTime = new Time(breakStart.getTime());
			Time breakEndTime = new Time(breakEnd.getTime());
			
			Map<String, Object> roomsConstraints = ConstraintsUtil.getConstraintsMap();
			roomsConstraints.put("status", true);
			
			List<Room> rooms =(List<Room>) RoomDao.getRooms(roomsConstraints);
					
			Map<String, Object> teacherConstraints = ConstraintsUtil.getConstraintsMap();
			teacherConstraints.put("currentStatus", true);	
			List teachers = TeacherDao.getTeachers(teacherConstraints);
			
			Collections.shuffle(teachers);
			
			List sections = SectionDao.getSections(UserUtil.currentUser().getBranch().getBranchId());
			
			System.out.println("Calling to make");
			
			String message = makeTimetable(sections, teachers, rooms, schoolStartTime, 
											schoolEndTime, breakStartTime, breakEndTime);
			System.out.println("called");
			
			return message;
	}
		
		public static List sortCourses(List<Course> courses, List<Teacher> teachers){
			
			List<Object> sorted = new ArrayList<Object>();
			List<Course> tempC = new ArrayList<Course>();

			for (Iterator iterator = teachers.iterator(); iterator.hasNext();) {
				Teacher teacher = (Teacher) iterator.next();
				List<Course> teacherCourses = new ArrayList(teacher.getCourses());
				for (Iterator iterator2 = courses.iterator(); iterator2.hasNext();) {
					Course c = (Course) iterator2.next();
Course:				for (Iterator iterator3 = teacher.getCourses().iterator(); iterator3.hasNext();) {
						Course course = (Course) iterator3.next();
						if(!(sorted.contains(c.getCourseId())) && course.getCourseId() == c.getCourseId()){
						
							tempC.add(c);
							sorted.add(c.getCourseId());
							break Course;
						}
					}
				}
			}
			
			return tempC;
		}
}

